﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.Drawing;  //for fonts and colors
using System.Diagnostics;
using System.Windows.Forms;
using System.IO; //For Stream

namespace DirectX_CS
{
	public class FontMesh : IModelTransformable, IManagedMaterial, IColorable, IRecreatable, IRenderCallable, INamed, IDisposable, IRangeable
	{
		[PGBrowsable]
		public Range3d Range
		{
			get { return new Range3d(XMin, YMin, ZMin, XMax, YMax, ZMax); }
		}

		bool manualRecreate = false;

		/// <summary>
		/// when setting multiple properties one after another better not recreate it each time just set this
		/// property to false and call Recreate manually. Then if you want or need, set it back to true.
		/// </summary>
		[PGBrowsable]
		public bool ManualRecreate
		{
			get { return manualRecreate; }
			set { manualRecreate = value;
			
				if (manualRecreate == false)
					Recreate();
			}
		}

		bool centerX = true;
		[PGBrowsable]
		public bool CenterX
		{
			get { return centerX; }
			set {
				if (centerX != value)
				{
					centerX = value;
					
					if (!manualRecreate)
						Recreate();
				}
				//vTransform.X = 0f;	//all X translation data is lost				
			}
		}
		bool centerY = true;
		[PGBrowsable]
		public bool CenterY
		{
			get { return centerY; }
			set
			{
				if (centerY != value)
				{
					centerY = value;
					if (!manualRecreate)
						Recreate();
				}
				
				//vTransform.Y = 0f;	   //all Y translation data is lost
			}
		}
		bool centerZ = true;
		[PGBrowsable]
		public bool CenterZ
		{
			get { return centerZ; }
			set
			{
				if (centerZ != value)
				{
					centerZ = value;
					if (!manualRecreate)
						Recreate();
				}
				//vTransform.Z = 0f;  //all Z translation data is lost
			}
		}
		GlyphMetricsFloat[] glyphMetrics;
		private float fontSize = 10;
		[PGBrowsable]
		public float FontSize
		{
			get { return fontSize; }
			set { fontSize = value;
				if (!manualRecreate)
					Recreate();
			}
		}
		private FontStyle fontStyle = FontStyle.Regular;
		[PGBrowsable]
		public FontStyle FontStyle
		{
			get { return fontStyle; }
			set { 
				fontStyle = value;
				if (!manualRecreate)
					Recreate();
			}
		}
		private GraphicsUnit graphicsUnit = GraphicsUnit.Pixel;
		[PGBrowsable]
		public GraphicsUnit GraphicsUnit
		{
			get { return graphicsUnit; }
			set { 
				graphicsUnit = value;
				if (!manualRecreate)
					Recreate();

			}
		}
		public FontMesh(bool createVisible)
		{
			this.shortNumber = ++creationCount;
			int scale = 10; 
			font = new System.Drawing.Font(FontFamily.GenericSerif, fontSize, fontStyle, graphicsUnit);
			fontTexture = TextureFromBMPResource("DirectX_CS", "Texture1.bmp", "Resources");	
			if (createVisible)
				Recreate();
			PrimitiveManager.Register(this);
		}
		public static Texture TextureFromBMPResource(string nspace, string bmpName, string folder)
		{
			System.Reflection.Assembly myAssembly = System.Reflection.Assembly.GetExecutingAssembly();
			string fullName = nspace + "." + folder + "." + bmpName;
			Stream myStream = myAssembly.GetManifestResourceStream(fullName);//myAssembly.GetManifestResourceStream(this.GetType(), texturename);
			Bitmap selectBoxTextureBMP = new Bitmap(myStream);
			Texture theTexture = new Texture(MainProgram.DxDevice, myStream, Usage.None, Pool.Managed);
			return theTexture;
		}

		~FontMesh()
		{
			this.DisposeInstance();
		}
		float defaultScale = 1f;

		//ref (taken from): see http://alt.pluralsight.com/wiki/default.aspx/Craig.DirectX/FontBasicsTutorial.html (accessed: Nov25,2009)
		protected void CreateObjects()
		{
			glyphMetrics = new GlyphMetricsFloat[text.Length];
			fontMesh = Mesh.TextFromFont(
				MainProgram.DxDevice,            // The device, of course
				font,       // The font we want to render with
				text,              // The text we want
				deviation,              // How "lumpy"? 
				thickness,             // How thick? 
				out glyphMetrics   // Information about the meshes 
			);

			//http://www.codegod.de/WebAppCodeGod/managed-directx-tutorial-meshes-AID149.aspx
			//it seems we need to recreate the vertex buffer once we set the initial values...

			Mesh tempMeshWithBuffer = fontMesh.Clone(fontMesh.Options.Value, CustomVertex.PositionNormalColored.Format, MainProgram.DxDevice);
			fontMesh.Dispose();
			fontMesh = tempMeshWithBuffer;

			Vector3 preModelTransformedBounds = ComputeMeshBounds(glyphMetrics);
			//Transform vTransform = new Transform();
			//vTransform.Scale = new Vector3(defaultScale, defaultScale, 1);
			//vTransform.RotationX = (float)(Math.PI / 2d);

			Vector3 tran = new Vector3(0,0,0);
			if (centerX)
				tran.X = -preModelTransformedBounds.X / 2f;
			if (centerY)
				tran.Y = -preModelTransformedBounds.Y / 2f;
			if (centerZ)
				tran.Z = -preModelTransformedBounds.Z / 2f;
			//Matrix mvm = vTransform.RecreateMatrix();
			//build a matrix in this order.. rotate, translate, then scale, remember to do this backwards
			Matrix scaling = Matrix.Scaling(new Vector3(defaultScale, defaultScale, 1));
			Matrix translation = Matrix.Translation(tran);
			Matrix rotation = Matrix.RotationX((float)(Math.PI / 2d));
			Matrix mvm = rotation * translation * scaling; //v = v * rnd * t * s ... v = rnd * t * s * v;
			int colorForVertices = mainVertexColor.ToArgb();

			CustomVertex.PositionNormalColored[] verts = (CustomVertex.PositionNormalColored[])fontMesh.VertexBuffer.Lock(
				0,
			   typeof(CustomVertex.PositionNormalColored),
				LockFlags.None,
			   fontMesh.NumberVertices);

			//also since we dont have the manager to do a "post creation transform" we will do it now
			Matrix userModelViewTransform = this.modelView.RecreateMatrix();

			//during transform compute xmin,max, ymin,max, zmin,max
			//dont start with 0, start with a point
			Vector3 pf = verts[0].Position; //first point, 
			Matrix combinedTransform = Matrix.Multiply(mvm, userModelViewTransform);
			pf.TransformCoordinate(combinedTransform);//and be sure to do the same translation that is happening in the loop below to all coordinates
			float xmin = pf.X, xmax = pf.X, ymin = pf.Y, ymax = pf.Y, zmin = pf.Z, zmax = pf.Z;
			for (int i = 0; i < verts.Length; i++)
			{
				//now we can modify and see the vertex buffer
				verts[i].Color = colorForVertices;
				Vector3 pos = verts[i].Position;
				pos.TransformCoordinate(combinedTransform);
				verts[i].Position = pos;
				//look for new max and mins
				if (pos.X > xmax) xmax = pos.X; 
				if (pos.X < xmin) xmin = pos.X;
				if (pos.Y > ymax) ymax = pos.Y; 
				if (pos.Y < ymin) ymin = pos.Y;
				if (pos.Z > zmax) zmax = pos.Z; 
				if (pos.Z < zmin) zmin = pos.Z;
			}
			//now we can set the real property values
			xMax = xmax; xMin = xmin;
			yMax = ymax; yMin = ymin;
			zMax = zmax; zMin = zmin;

			fontMesh.VertexBuffer.Unlock();
			fontMesh.ComputeNormals();
			Optimize();
			SetMeshAttributeTable();
		}
	
		System.Drawing.Font font;
		[PGBrowsable]
		public System.Drawing.Font Font
		{
			get { return font; }
			set { font = value;
			if (!manualRecreate)
				Recreate();

			}
		}
		string text = "3d Text";
		[PGBrowsable]
		public string Text
		{
			get { return text; }
			set { text = value;
			if (!manualRecreate)
				Recreate();

			}
		}

		float thickness = .1f;
		//[PGBrowsable]
		//public float Thickness
		//{
		//    get { return thickness; }
		//    set { thickness = value;
		//    if (!manualRecreate)
		//        Recreate();

		//    }
		//}
		float deviation = .01f;//??
		[PGBrowsable]
		public float Deviation
		{
			get { return deviation; }
			set { deviation = value;
			if (!manualRecreate)
				Recreate();

			}
		}
		//Vector3 meshBounds = new Vector3(0, 0, 0);
		//[PGBrowsable]
		//public Vector3 MeshBounds
		//{
		//    get { return meshBounds; }
		//}
		Mesh fontMesh = null;
		[PGBrowsable]
		public Mesh Mesh
		{
			get { return fontMesh; }
		}
		AttributeRange attributeRange = new AttributeRange();
		AttributeRange[] AR_Array = new AttributeRange[1];

		private void SetMeshAttributeTable()
		{
			attributeRange.AttributeId = 0; //DONT FORGET THIS
			attributeRange.VertexStart = 0;
			attributeRange.VertexCount = fontMesh.NumberVertices;
			attributeRange.FaceStart = 0;
			attributeRange.FaceCount = fontMesh.NumberFaces;
			AR_Array[0] = attributeRange;
			fontMesh.LockAttributeBufferArray(LockFlags.None);
			fontMesh.SetAttributeTable(AR_Array);
			fontMesh.UnlockAttributeBuffer();
		}
		private Vector3 ComputeMeshBounds(GlyphMetricsFloat[] gmfs)
		{
			float maxx = 0;
			float maxy = 0;
			float offsety = 0;
			foreach (GlyphMetricsFloat gmf in gmfs)
			{
				maxx += gmf.CellIncX;
				float y = offsety + gmf.BlackBoxY;
				if (y > maxy)
				{
					maxy = y;
				}
				offsety += gmf.CellIncY;
			}
			Vector3 bounds = new Vector3(maxx, maxy, thickness);
			return bounds;
		}

		#region IRecreatable Members

		public void Recreate()
		{
			CreateObjects();
		}

		#endregion


		#region IRenderCallable Members

		protected bool preRenderEnabled = true;
		[PGBrowsable]
		public bool PreRenderEnabled
		{
			get
			{
				return preRenderEnabled;
			}
			set
			{
				preRenderEnabled = value;
			}
		}
		[PGBrowsable]
		public bool PostRenderEnabled
		{
			get
			{
				return false;
			}
			set
			{
				//do nothing since this should never change because we dont implement or need PostRender
			}
		}
		Texture fontTexture = null;
		[PGBrowsable]
		public Texture FontTexture
		{
			get { return fontTexture; }
			set { fontTexture = value;
			}
		}
		
		public void PreRender()
		{
			if (fontMesh != null)
			{
				//MainProgram.DxDevice.Material = this.material;
				MainProgram.DxDevice.SetTexture(0, this.fontTexture);

				fontMesh.DrawSubset(attributeRange.AttributeId);   //0 is of course the attribute id of the attributerange, specified

				MainProgram.DxDevice.SetTexture(0, null);
			}
		}

		public void PostRender()
		{
		}

		public void ManagerDestroyed(PrimitiveManager caller)
		{
		}

		#endregion

		#region IColorable Members

		protected Color mainVertexColor = Color.DarkGreen;
		[PGBrowsable]
		public Color MainVertexColor
		{
			get
			{
				return mainVertexColor;
			}
			set
			{
				mainVertexColor = value;
				if (!manualRecreate)
					Recreate();

			}
		}

		#endregion

		#region IManagedMaterial Members
		Material material = new Material();
		[PGBrowsable]
		public Material Material
		{
			get
			{
				return material;
			}
			set
			{
				material = value;
			}
		}

		#endregion

		#region INamed Members

		string shortName = "MeshFont3d";
		public string ShortName
		{
			get
			{
				return shortName;
			}
			set
			{
				shortName = value;
			}
		}

		static int creationCount = 0;
		int shortNumber = -1;
		public int ShortNumber
		{
			get
			{
				return shortNumber;
			}
			set
			{
				shortNumber = value;
			}
		}
		[PGBrowsable]
		public string ObjectName
		{
			get { return shortName + " " + shortNumber; }
		}

		#endregion
		[PGBrowsable]
		public int NumberFaces
		{
			get
			{
				if (fontMesh != null)
					return fontMesh.NumberFaces;
				else
					return 0;
			}
		}
		[PGBrowsable]
		public int NumberVertices
		{
			get
			{
				if (fontMesh != null)
					return fontMesh.NumberVertices;
				else
					return 0;
			}
		}
		private WeldEpsilons epsilons = new WeldEpsilons();
		[PGBrowsable]
		public WeldEpsilons Epsilons
		{
			get { return epsilons; }
			set
			{
				epsilons = value; 
				if (!manualRecreate)
					Recreate();
			}
		}
		private void Optimize()
		{
			int[] adjacency;
			epsilons.Position = .1f;
			int numFaces = fontMesh.NumberFaces;

			adjacency = new int[numFaces * 3];
			fontMesh.GenerateAdjacency(0.1F, adjacency);

			fontMesh.OptimizeInPlace(
				MeshFlags.OptimizeCompact | 
				MeshFlags.OptimizeVertexCache, 
				adjacency);
		}


		#region IDisposable Members

		public void DisposeInstance()
		{
			PrimitiveManager.Unregister(this);
			fontTexture.Dispose();
			font.Dispose();
			fontMesh.Dispose();	
		}

		#endregion

		#region IRangeableOld Members

		protected float xMin;
		[PGBrowsable]
		public float XMin
		{
			get
			{
				return xMin;
			}

		}

		protected float xMax;
		[PGBrowsable]
		public float XMax
		{
			get
			{
				return xMax;
			}
		}

		protected float yMin;
		[PGBrowsable]
		public float YMin
		{
			get
			{
				return yMin;
			}
		}

		protected float yMax;
		[PGBrowsable]
		public float YMax
		{
			get
			{
				return yMax;
			}
		}
		protected float zMin;
		[PGBrowsable]
		public float ZMin
		{
			get
			{
				return zMin;
			}
		}

		protected float zMax;
		[PGBrowsable]
		public float ZMax
		{
			get
			{
				return zMax;
			}
		}

		#endregion

		#region IModelTransformable Members

		TransformMatrix modelView = new TransformMatrix();
		public TransformMatrix ModelView
		{
			get
			{
				return modelView;
			}
			set
			{
				modelView = value;
			}
		}

		#endregion
	}
}
